package com.h.config;

import com.h.annotation.Handler;
import com.h.api.TransferProtocol;
import com.h.autoconfigure.NettyConfigProperties;
import com.h.util.NetUtil;
import com.h.util.NettyCreatorUtil;
import io.netty.bootstrap.AbstractBootstrap;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.EventLoop;
import io.netty.channel.socket.nio.NioDatagramChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.*;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;

import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * @author ：Hukaiwen
 * @description ：监听容器刷新完成事件,扫描所有@Handler注释的bean对象,创建服务端
 * @date ：2022/2/22 14:07
 */
public class AutoStartApplicationInitializer
        implements ApplicationContextInitializer<ConfigurableApplicationContext>, ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware, Ordered {

    private static final Logger log = LoggerFactory.getLogger(AutoStartApplicationInitializer.class);

    private ApplicationContext applicationContext;

    private int order = Ordered.LOWEST_PRECEDENCE;

    public void setOrder(int order) {
        this.order = order;
    }

    @Override
    public int getOrder() {
        return order;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //找到根上下文
        this.applicationContext = applicationContext;
    }

    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        configurableApplicationContext.addApplicationListener(this);
    }

    private void reconnect(ChannelFuture channelFuture, AbstractBootstrap<?, ?> bootstrap, NettyConfigProperties.Configuration config) {
        EventLoop loop = channelFuture.channel().eventLoop();
        loop.schedule(() -> {
            try {
                ((Bootstrap) bootstrap).connect().channel().closeFuture().addListener((ChannelFutureListener) future -> {
//                    if (channelFuture.cause() != null) {
                        reconnect(channelFuture, bootstrap, config);
//                    }
                });
            } catch (Exception e) {
                reconnect(channelFuture, bootstrap, config);
            }
        }, config.getReconnect(), TimeUnit.SECONDS);
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
//        if (event.getApplicationContext().equals(this.applicationContext)) {
            ApplicationContext applicationContext = event.getApplicationContext();
            final Map<String, Object> handlerBeans = applicationContext.getBeansWithAnnotation(Handler.class);
            final NettyConfigProperties nettyConfigProperties = applicationContext.getBean(NettyConfigProperties.class);
            final ChannelFactory channelFactory = applicationContext.getBean(ChannelFactory.class);
            channelFactory.setExecutorService(new ThreadPoolExecutor(1, handlerBeans.size() + 1, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
            handlerBeans.forEach((k, v) -> {
                if (v instanceof ChannelHandler) {
                    final String name = v.getClass().getAnnotation(Handler.class).value();
                    final List<NettyConfigProperties.Configuration> configs = nettyConfigProperties.getConfigurations().stream().filter(m -> m.getName().equals(name)).collect(Collectors.toList());
                    if (configs.isEmpty()) {
                        log.error(String.format("=====未找到Handler：%s对应的配置=====", name));
                        return;
                    } else if (configs.size() > 1) {
                        log.error(String.format("=====找到Handler：%s对应的多条配置,请检查配置文件是否正确=====", name));
                        return;
                    }
                    NettyConfigProperties.Configuration config = configs.get(0);
                    if (!config.isAvailable()) {
                        return;
                    }
                    AbstractBootstrap<?, ?> bootstrap = null;
                    //创建Bootstrap
                    try {
                        bootstrap = NettyCreatorUtil.create(config, (ChannelHandler) v);
                    } catch (UnknownHostException e) {
                        log.error(String.format("无法解析的主机地址:%s", config), e);
                    } catch (InstantiationException | IllegalAccessException e) {
                        log.error(String.format("%s缺少无参构造函数或无权访问无参构造函数", config.getBootstrapStrategy()), e);
                    } catch (Exception e) {
                        log.error("创建引导类时发生错误", e);
                    }
                    if (bootstrap == null) {
                        return;
                    }
                    final AbstractBootstrap<?, ?> finalBootstrap = bootstrap;

                    final FutureTask<ChannelFuture> task = new FutureTask<>(() -> {
                        ChannelFuture channelFuture;
                        if (config.getProtocol() == TransferProtocol.TCP_CLIENT
                                || config.getProtocol() == TransferProtocol.WEBSOCKET_CLIENT) {
                            channelFuture = ((Bootstrap) finalBootstrap).connect();
                            channelFuture.channel().closeFuture().addListener((ChannelFutureListener) bindFuture -> {
                                reconnect(channelFuture, finalBootstrap, config);
                            });
                        } else {
                            channelFuture = finalBootstrap.bind();
                            if (config.getProtocol() == TransferProtocol.UDP_MULTICAST) {
                                channelFuture.addListener((ChannelFutureListener) bindFuture -> {
                                    InetSocketAddress multicast = new InetSocketAddress(config.getMulticastAddress().getHost(), config.getMulticastAddress().getPort());
                                    NetworkInterface networkInterface = NetUtil.getNetworkInterface(NetUtil.getInetAddress(config.getLocalAddress().getHost()));
                                    ((NioDatagramChannel) bindFuture.channel()).joinGroup(multicast, networkInterface).sync();
                                    log.info(String.format("=====名称：%s,成功监听%s端口,成功加入组播地址：%s,传输协议：%s=====", config.getName(), config.getMulticastAddress().getPort(), multicast, config.getProtocol().name()));
                                    final ChannelFuture cf = bindFuture.channel().closeFuture();
                                    cf.addListener((ChannelFutureListener) closeFuture -> {
                                        ((NioDatagramChannel) closeFuture.channel()).leaveGroup(multicast, networkInterface).sync();
                                        log.info(String.format("=====名称：%s,离开组播地址：%s,传输协议：%s=====", config.getName(), multicast, config.getProtocol().name()));
                                        finalBootstrap.config().group().shutdownGracefully();
                                    });
                                });
                            } else {
                                channelFuture.addListener((ChannelFutureListener) bindFuture -> {
                                    switch (config.getProtocol()) {
                                        case WEBSOCKET:
                                            log.info(String.format("=====名称：%s,成功监听%s%s,传输协议：%s=====", config.getName(), config.getLocalAddress(), config.getLocalAddress().getPath(), config.getProtocol().name()));
                                            break;
                                        default:
                                            log.info(String.format("=====名称：%s,成功监听%s端口,传输协议：%s=====", config.getName(), config.getLocalAddress().getPort(), config.getProtocol().name()));
                                            break;
                                    }
                                    bindFuture.channel().closeFuture().addListener((ChannelFutureListener) closeFuture -> {
                                        finalBootstrap.config().group().shutdownGracefully();
                                        log.info(String.format("=====名称：%s,离开地址：%s,传输协议：%s=====", config.getName(), config.getLocalAddress(), config.getProtocol().name()));
                                    });
                                });
                            }
                        }
                        return channelFuture;
                    });
                    if (channelFactory.getChannels().containsKey(name)) {
                        log.error(String.format("找到多个相同名称的Handler：%s,请检查@Handler注解", name));
                    } else {
                        channelFactory.getChannels().put(name, task);
                        channelFactory.getHandlers().put(name, (ChannelHandler) v);
                        channelFactory.getConfigs().put(v.getClass(), config);
                        channelFactory.getExecutorService().submit(task);
                    }
                }
            });
        }
//    }
}

